<?php

namespace app\common\library\wechat;

use app\common\library\MysicLog;
use app\common\model\Order;
use app\common\model\Wxapp as WxappModel;
use app\common\enum\OrderType as OrderTypeEnum;
use app\common\enum\order\PayType as PayTypeEnum;
use app\common\exception\BaseException;
use app\common\mysic\notify\entity\WeiXinEntity;
use think\Env;
use think\Exception;

use think\Request;

/**
 * 微信支付
 * Class WxPay
 * @package app\common\library\wechat
 */
class WxPay extends WxBase
{
    // 微信支付配置
    private $config;
    private $mysicLog;
    private $wxSign;
    private $response = [];
    // 订单模型
    private $modelClass = [
        OrderTypeEnum::MASTER => 'app\api\service\order\PaySuccess',
        OrderTypeEnum::SHARING => 'app\api\service\sharing\order\PaySuccess',
        OrderTypeEnum::RECHARGE => 'app\api\service\recharge\PaySuccess',
        OrderTypeEnum::VIPPAY => 'app\api\service\vippay\PaySuccess',
        OrderTypeEnum::GIFTSPAY => 'app\api\service\gifts\PaySuccess',
        OrderTypeEnum::FACEPAY => 'app\api\service\order\PaySuccess',
        OrderTypeEnum::ADMINPAY => 'app\api\service\large\PaySuccess'
    ];

    /**
     * 构造函数
     * WxPay constructor.
     * @param $config
     */
    public function __construct($config = false)
    {

        parent::__construct();
        $this->config = $config;
        $this->config !== false && $this->setConfig($this->config['app_id'], $this->config['app_secret']);
        $this->mysicLog = MysicLog::instance();
        $this->wxSign = new WxSign($this->config['app_id'], $this->config['app_secret']);
    }

    public function setConfigArr(array $config)
    {
        $this->config = $config;
        !empty($this->config) && $this->setConfig($this->config['app_id'], $this->config['app_secret']);
    }


    public function getResponse()
    {
        return $this->response;
    }






    /**
     * 大额支付成功异步通知
     * @throws BaseException
     * @throws \Exception
     * @throws \think\exception\DbException
     */
    public function denotify()
    {

        $this->mysicLog->setFilename('notify/large');


        try {
            if (!$xml = Request::instance()->getContent()) {
                $this->returnCode(false, 'Not found DATA');
            }
            $this->mysicLog->info($xml, '微信支付回调XML');

            // 将服务器返回的XML数据转化为数组
            $data = $this->fromXml($xml);
            $this->mysicLog->info($data, '微信支付回调数据');
            $this->mysicLog->info($data['out_trade_no'], '订单号');

            $this->mysicLog->info('开始','实例化订单模型');

            $model = $this->getOrderModel($data['out_trade_no'], $data['attach']);
            // 订单信息
            $this->mysicLog->info('开始','获取订单信息');

            $order = $model->getOrderInfo();
            if(empty($order)) {
                $this->mysicLog->info('订单不存在', '业务结束');
                $this->returnCode(false, '订单不存在');
            }

            $this->mysicLog->info($order, '获取表中存储的订单信息');

            // 小程序配置信息
            $this->mysicLog->info('开始','获取小程序配置信息');

            $wxConfig = db('base_setting')->where(['key'=>'largepay'])->value('values');
            $wxConfig = json_decode($wxConfig,true);

            // 设置支付秘钥
            $this->config['apikey'] = $wxConfig['apikey'];
            // 保存微信服务器返回的签名sign
            $dataSign = $data['sign'];
            // sign不参与签名算法
            unset($data['sign']);
            // 生成签名
            $sign = $this->makeSign($data);
            // 判断签名是否正确 判断支付状态
            if (
                ($sign !== $dataSign)
                || ($data['return_code'] !== 'SUCCESS')
                || ($data['result_code'] !== 'SUCCESS')
            ) {
                $this->mysicLog->info($sign,'签名失败');
                $this->mysicLog->info($dataSign,'签名失败');
                $this->mysicLog->info('签名失败','签名失败');
                $this->returnCode(false, '签名失败');
            }
            // 订单支付成功业务处理
            $status = $model->onPaySuccess(PayTypeEnum::WECHAT, $data);
            $this->mysicLog->info($status,'回调处理结果');
            if ($status == false) {
                $this->mysicLog->info('','回调处理111');
                $this->returnCode(false, $model->getError());
                $this->mysicLog->info('','回调处理333');
            }

            $this->mysicLog->info('','回调处理完成');
            // 返回状态
            return $this->returnCode(true, 'OK');
        } catch(\Throwable $t) {
            $this->mysicLog->info($t->getMessage(),'微信支付回调异常');
        }

    }


    /**
     * 支付成功异步通知
     * @throws BaseException
     * @throws \Exception
     * @throws \think\exception\DbException
     */
    public function notify()
    {
        $this->mysicLog->setFilename('notify/order');
        try {
            if (!$xml = Request::instance()->getContent()) {
                $this->returnCode(false, 'Not found DATA');
            }
            $this->mysicLog->info($xml, '微信支付回调XML');
            $this->log('微信支付回调原始XML： ', $xml);
            // 将服务器返回的XML数据转化为数组
            $data = $this->fromXml($xml);
            // 记录日志
            $this->doLogs($xml);
            $this->doLogs($data);
            $this->mysicLog->info($data['out_trade_no'], '订单号');
            $this->log('实例化订单模型');
            // 实例化订单模型
            $this->log('out_trade_no......:'.$data['out_trade_no']);
            $this->log('attach......:'.$data['attach']);

            try {
                $model = $this->getOrderModel($data['out_trade_no'], $data['attach']);
                // 订单信息
                $this->log('获取订单信息......');
                $order = $model->getOrderInfo();
            } catch(\Exception $e) {
                $order = null;
                $this->mysicLog->error(exceptionLogFormat($e), '获取订单信息时异常');
            }
            if(empty($order)) {
                $commonOrderModel = $this->getCommonOrderModel($data['attach']);
                $originOrder = $commonOrderModel->where('order_no',$data['out_trade_no'])->find();
                if(empty($originOrder)) {
                    $this->mysicLog->info('订单不存在', '业务结束');
                    $this->returnCode(false, '订单不存在');
                }
                $originOrder = $originOrder->toArray();
                if($originOrder['pay_status']['value'] == 20) {
                    $this->mysicLog->info('订单已支付', '业务结束');
                    $this->returnCode(true, 'OK');
                }
            }

            $order = $order->getData();
            $this->mysicLog->info(json_encode($order,JSON_UNESCAPED_UNICODE), '获取表中存储的订单信息');
            $this->log('订单信息', $order);
            // 小程序配置信息
            $this->log('获取小程序配置信息...');
            $wxConfig = WxappModel::getWxappCache($order['wxapp_id']);

            // 设置支付秘钥
            $this->config['apikey'] = $wxConfig['apikey'];
            // 保存微信服务器返回的签名sign
            $dataSign = $data['sign'];
            // sign不参与签名算法
            unset($data['sign']);
            // 生成签名
            $sign = $this->makeSign($data);
            // 判断签名是否正确 判断支付状态
            if (
                ($sign !== $dataSign)
                || ($data['return_code'] !== 'SUCCESS')
                || ($data['result_code'] !== 'SUCCESS')
            ) {
                $this->log('签名失败',['sign' => $sign, 'originSign' => $dataSign]);
                $this->returnCode(false, '签名失败');
            }
            // 订单支付成功业务处理
            $status = $model->onPaySuccess(PayTypeEnum::WECHAT, $data);
            $this->log('回调处理结果',$status);
            if ($status == false) {
                $this->log('回调处理111');
                $this->returnCode(false, $model->getError());
                $this->log('回调处理333');
            }
            //添加分账记录表
            $this->log('回调处理完成');
            // 返回状态
            return $this->returnCode(true, 'OK');
        } catch(\Throwable $t) {
            $this->log('微信支付回调异常', ['msg' => $t->getMessage(),'file' => $t->getFile(),'line' => $t->getLine()]);
        }

    }

    protected function log($pre,$data = null)
    {
        $fileName = 'refund-'.date('Ymd', time()).'.log';
        if(!file_exists($fileName)) {
            if($data) {
                $data = is_array($data) ? json_encode($data) : $data;
                file_put_contents($fileName,  $pre . $data .  PHP_EOL, FILE_APPEND);
            } else {
                file_put_contents($fileName,  $pre .  PHP_EOL, FILE_APPEND);
            }
            chmod($fileName, 0766);
        }else {
            if($data) {
                $data = is_array($data) ? json_encode($data) : $data;
                file_put_contents($fileName,  $pre . $data .  PHP_EOL, FILE_APPEND);
            } else {
                file_put_contents($fileName,  $pre .  PHP_EOL, FILE_APPEND);
            }
        }

    }

    /**
     * 统一下单API
     * @param $order_no
     * @param $openid
     * @param $totalFee
     * @param int $orderType 订单类型
     * @return array
     * @throws BaseException
     */

    public function unifiedorder($order_no, $openid, $totalFee, $orderType = OrderTypeEnum::MASTER)
    {
        if($orderType==40){
            $order=db('gifts_order')->where('order_no',$order_no)->find();
            if(empty($order)){
                throw new BaseException(['msg' => '下单超时，请重新下单', 'code' => -10]);

            }
            $profit_sharing='N';
        }else{
            $order=db('order')->where('order_no',$order_no)->find();
            $setting = db('wxapp_setting')->where('wxapp_id', $order['wxapp_id'])->find();
            if(empty($order)){

                throw new BaseException(['msg' => '下单超时，请重新下单', 'code' => -10]);

            }
            if($order['is_fictitious']==1 || $setting['real_reward'] == 1){
                $profit_sharing='Y';
            }else{
                $profit_sharing='N';
            }
        }
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time . $openid);
        // API参数
        $params = [
            'appid' => $this->appId,
            'attach' => json_encode(['order_type' => $orderType]),
            'body' => $order_no,
            'mch_id' => $this->config['mchid'],
            'nonce_str' => $nonceStr,
            'notify_url' => base_url() . 'notice.php',  // 异步通知地址
            'openid' => $openid,
            'profit_sharing'=>$profit_sharing,
            //'profit_sharing'=>'Y',
            'out_trade_no' => $order_no,
            'spbill_create_ip' => \request()->ip(),
            'total_fee' => bcmul(strval($totalFee), '100'), // 价格:单位分
            'trade_type' => 'JSAPI',
        ];

        // 生成签名
        $params['sign'] = $this->makeSign($params);

        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->fromXml($result);
        $this->mysicLog->info($prepay, '调用微信统一下单接口返回值');
        $responseSign = $prepay['sign'] ?? '';
        $this->mysicLog->info($responseSign, '微信接口返回值签名');
        unset($prepay['sign']);
        $signValues = $prepay;

        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg'], 'code' => -10]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => -10]);
        }
        try{
            $localSign = $this->makeSign($signValues);
            $this->mysicLog->info($localSign, '根据微信接口返回值生成本地签名');
        } catch(\Exception $e) {
            throw $e;
        }
        if($localSign !== $responseSign) {
            $this->mysicLog->error('', '微信接口返回数据签名不合法');
            return [];
        }
        // 生成 nonce_str 供前端使用
        $paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time);
        return [
            'prepay_id' => $prepay['prepay_id'],
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$time,
            'paySign' => $paySign,
            'appid'=>$this->appId
        ];
    }

    /**
     *  native模式下的支付
     * @param $order_no
     * @param $totalFee
     * @param int $orderType
     * @return array
     * @throws BaseException
     */
    public function unifiedordernative($order_no, $totalFee,$orderType = OrderTypeEnum::MASTER)
    {

        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time);

        // API参数
        $params = [
            'appid' => $this->appId,
            'attach' => json_encode(['order_type' => $orderType]),
            'body' => $order_no,
            'mch_id' => $this->config['mchid'],
            //'sub_mch_id' => $sub_mchid,
            'nonce_str' => $nonceStr,
            'notify_url' => base_url() . 'denotice.php',  // 异步通知地址
            //  'openid' => $openid,
            //'profit_sharing'=>'Y',
            'out_trade_no' => $order_no,
            'spbill_create_ip' => \request()->ip(),
            'total_fee' => bcmul(strval($totalFee), '100'), // 价格:单位分
            'trade_type' => 'NATIVE',
        ];

        // 生成签名
        $params['sign'] = $this->makeSign($params);

        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->fromXml($result);

        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg'], 'code' => -10]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => -10]);
        }

        return  $prepay;

        // 生成 nonce_str 供前端使用
        //$paySign = $this->makeNativePaySign($params['nonce_str'], $prepay['prepay_id'], $time);


//        return [
//            'prepay_id' => $prepay['prepay_id'],
//            'nonceStr' => $nonceStr,
//            'timeStamp' => (string)$time,
//            'paySign' => $paySign
//        ];
    }




    public function orderquery($order_no){
        // 当前时间
        $time = time();
        // 生成随机字符串
        $nonceStr = md5($time);
        // API参数
        $params = [
            'appid' => $this->appId,
            'mch_id' => $this->config['mchid'],
            'nonce_str' => $nonceStr,
            'out_trade_no' => $order_no,
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg'], 'code' => -10]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['err_code_des'], 'code' => -10]);
        }

    }

    /**
     * 申请退款API
     * @param $transaction_id
     * @param  double $total_fee 订单总金额
     * @param  double $refund_fee 退款金额
     * @param  string $profitSharingSn
     * @return bool
     * @throws BaseException
     */
    public function refund($transaction_id, $total_fee, $refund_fee, $profitSharingSn = '')
    {
        $this->mysicLog->info('开始', '调用微信退款接口');
        $this->log('退款微信订单号',$transaction_id);
        if($profitSharingSn) {
            $outRefundNo = $profitSharingSn;
        } else {
            // 当前时间
            $outRefundNo = date('YmdHis') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8).$transaction_id.rand(0,9999);
        }

        $time=time();
        // 生成随机字符串
        $nonceStr = md5($time . $transaction_id . $total_fee . $refund_fee);
        // API参数
        $params = [
            'appid' => $this->appId,
            'mch_id' => $this->config['mchid'],
            'nonce_str' => $nonceStr,
            'transaction_id' => $transaction_id,
            'out_refund_no' =>$outRefundNo,
            'total_fee' => bcmul(strval($total_fee), '100'),
            'refund_fee' => bcmul(strval($refund_fee),'100'),
        ];

        // 生成签名
        $params['sign'] = $this->makeSign($params);
        $this->log('退款请求参数',$params);
        $this->mysicLog->info($params, '调用微信退款接口入参');
        $url ='https://api.mch.weixin.qq.com/secapi/pay/refund';
        $result=$this->post($url, $this->toXml($params), true, $this->getCertPem());
        $this->log('退款返回内容',$result);
        // 请求失败
        if (empty($result)) {
            throw new BaseException(['msg' => '微信退款api请求失败']);
        }
        // 格式化返回结果
        $prepay = $this->fromXml($result);
        $this->response = $prepay;
        $this->mysicLog->info($prepay, '调用微信退款接口返回值');
        $responseSign = $prepay['sign'] ?? '';
        if(!$responseSign) {
            return false;
        }
        $this->mysicLog->info($responseSign, '微信接口返回值签名');
        unset($prepay['sign']);
        $signValues = $prepay;
        try{
            $localSign = $this->makeSign($signValues);
            $this->mysicLog->info($localSign, '根据微信接口返回值生成本地签名');
        } catch(\Exception $e) {
            throw $e;
        }
        if($localSign !== $responseSign) {
            throw new BaseException(['msg' => '微信接口返回数据签名不合法', 'code' => -10]);
        }

        // 请求失败
        if ($prepay['return_code'] == 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg']]);
        }
        if ($prepay['result_code'] == 'FAIL' && $prepay['err_code'] != 'FREQUENCY_LIMITED') {
            throw new BaseException(['msg' => $prepay['err_code_des']]);
        }
        if($prepay['result_code'] == 'FAIL' && $prepay['err_code'] == 'FREQUENCY_LIMITED') {
            sleep(1);
            $prepay = $this->refund($transaction_id, $total_fee, $refund_fee);
            if($prepay['return_code'] == 'SUCCESS' && $prepay['result_code'] == 'SUCCESS') {
                return $prepay;
            }
        }
        return $prepay;
    }

    public function refundQuery($transaction_id, $nonce1 = 'nonce_ldtc1', $nonce2 = 'nonce_ldtc2', $page = 1)
    {
        $time=time();
        $nonceStr = md5($time . $transaction_id . $nonce1 . $nonce2);
        $params = [
            'appid' => $this->appId,
            'mch_id' => $this->config['mchid'],
            'nonce_str' => $nonceStr,
            'transaction_id' => $transaction_id
        ];
        if($page > 1) {
            $params['offset'] = ($page - 1) * 10;
        }
        $sign = $this->makeSign($params);
        $params['sign'] = $sign;

        $url = 'https://api.mch.weixin.qq.com/pay/refundquery';
        $ret = $this->post($url, $this->toXml($params));
        $ret = $this->fromXml($ret);
        $this->response = $ret;
        $this->mysicLog->info($ret, '微信退款查询调用返回结果');
        $responseSign = $ret['sign'] ?? '';
        if(!$responseSign) {
            return false;
        }
        $this->mysicLog->info($responseSign, '微信接口返回值签名');
        unset($ret['sign']);
        $signValues = $ret;
        try{
            $localSign = $this->makeSign($signValues);
            $this->mysicLog->info($localSign, '根据微信接口返回值生成本地签名');
        } catch(\Exception $e) {
            throw $e;
        }
        if($localSign !== $responseSign) {
            throw new BaseException(['msg' => '微信接口返回数据签名不合法', 'code' => -10]);
        }

        if($ret['return_code'] != 'SUCCESS') {
            throw new Exception($ret['return_msg']);
        }
        if($ret['result_code'] != 'SUCCESS') {
            throw new Exception($ret['err_code_des']);
        }
        if($ret['result_code'] == 'SUCCESS') {
//            if($ret['refund_status_' . $refundSeq] == 'REFUNDCLOSE' || $ret['refund_status_' . $refundSeq] == 'CHANGE'){
//                throw new Exception($ret['err_code_des']);
//            }
//            if(!in_array($ret['refund_status_'. $refundSeq], ['SUCCESS', 'PROCESSING'])) {
//                return false;
//            }
        }
        return true;



    }

    /**
     * 企业付款到零钱API
     * @param $order_no
     * @param $openid
     * @param $amount
     * @param $desc
     * @return bool
     * @throws BaseException
     */
    public function transfers($order_no, $openid, $amount, $desc)
    {
        // API参数
        $params = [
            'mch_appid' => $this->appId,
            'mchid' => $this->config['mchid'],
            'nonce_str' => md5(uniqid()),
            'partner_trade_no' => $order_no,
            'openid' => $openid,
            'check_name' => 'NO_CHECK',
            'amount' => bcmul(strval($amount), '100'),
            'desc' => $desc,
            'spbill_create_ip' => \request()->ip(),
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params);

        // 请求API
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';

        $result = $this->post($url, $this->toXml($params), true, $this->getCertPem());

        // 请求失败
        if (empty($result)) {
            throw new BaseException(['msg' => '微信退款api请求失败']);
        }
        // 格式化返回结果
        $prepay = $this->fromXml($result);
        // 请求失败
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]);
        }
        if ($prepay['result_code'] === 'FAIL') {
            throw new BaseException(['msg' => 'err_code_des: ' . $prepay['err_code_des']]);
        }
        return true;
    }

    /**
     * 获取cert证书文件
     * @return array
     * @throws BaseException
     */
    private function getCertPem()
    {
        if (empty($this->config['cert_pem']) || empty($this->config['key_pem'])) {
            throw new BaseException(['msg' => '请先到后台小程序设置填写微信支付证书文件']);
        }
        // cert目录
        $filePath = __DIR__ . '/cert/' . $this->config['wxapp_id'] . '/';
        return [
            'certPem' => $filePath . 'cert.pem',
            'keyPem' => $filePath . 'key.pem'
        ];
    }


    /**
     * 实例化订单模型 (根据attach判断)
     * @param $orderNo
     * @param null $attach
     * @return mixed
     */
    private function getOrderModel($orderNo, $attach = null)
    {
        $attach = json_decode($attach, true);
        // 判断订单类型返回对应的订单模型
        $model = $this->modelClass[$attach['order_type']];
        return new $model($orderNo);
    }

    private function getCommonOrderModel($attach = OrderTypeEnum::MASTER)
    {
        $commonOrderModelList = [
            OrderTypeEnum::MASTER => \app\common\model\Order::class,
            OrderTypeEnum::SHARING => \app\common\model\sharing\Order::class,
            OrderTypeEnum::RECHARGE => \app\common\model\recharge\Order::class,
            OrderTypeEnum::VIPPAY => \app\common\model\vipcard\Order::class,
            OrderTypeEnum::GIFTSPAY => \app\common\model\gifts\Order::class,
            OrderTypeEnum::FACEPAY => \app\common\model\Order::class
        ];
        if($attach = json_decode($attach, true)) {
            return new $commonOrderModelList[$attach['order_type']];
        }
        return new $commonOrderModelList[OrderTypeEnum::MASTER];
    }


    /**
     * 返回状态给微信服务器
     * @param boolean $returnCode
     * @param string $msg
     */
    private function returnCode($returnCode = true, $msg = null)
    {
        // 返回状态
        $return = [
            'return_code' => $returnCode ? 'SUCCESS' : 'FAIL',
            'return_msg' => $msg ?: 'OK',
        ];
        $this->log('回调处理222',$return);
        // 记录日志
//        log_write([
//            'describe' => '返回微信支付状态',
//            'data' => $return
//        ]);
        if(Env::get('mock.pay')) {
            return $this->toXml($return);
        }

        die($this->toXml($return));
    }



    /**
     * 生成paySign
     * @param $nonceStr
     * @param $prepay_id
     * @param $timeStamp
     * @return string
     */
    private function makePaySign($nonceStr, $prepay_id, $timeStamp)
    {
        $data = [
            'appId' => $this->appId,
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
            'timeStamp' => $timeStamp,
        ];
        // 签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        // 签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['apikey'];
        // 签名步骤三：MD5加密
        $string = md5($string);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 将xml转为array
     * @param $xml
     * @return mixed
     */
    private function fromXml($xml)
    {
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 生成签名
     * @param $values
     * @return string 本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    private function makeSign($values)
    {

        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['apikey'];

        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }




    /**
     * 格式化参数格式化成url参数
     * @param $values
     * @return string
     */
    private function toUrlParams($values)
    {
        $buff = '';
        foreach ($values as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }
        return trim($buff, '&');
    }

    /**
     * 输出xml字符
     * @param $values
     * @return bool|string
     */
    private function toXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

}
